package org.elzat.healthboxgatewayapp.controller;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.elzat.healthboxgatewayapp.dto.HealthBoxUser;
import org.elzat.healthboxgatewayapp.dto.HealthBoxUserVO;
import org.elzat.healthboxgatewayapp.util.ResultEntity;
import org.elzat.healthboxgatewayapp.security.HealthBoxJwt;
import org.elzat.healthboxgatewayapp.service.HealthBoxUserDetailsService;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

@AllArgsConstructor
@RestController
@RequestMapping(value = "health-box-api/v1/login",
        produces = "application/json; charset=utf-8")
@Slf4j
public class AuthenticationController {

    private final HealthBoxJwt healthBoxJwt;
    private final PasswordEncoder passwordEncoder;
    private final HealthBoxUserDetailsService userDetailsService;

    /**
     * 登录接口
     * POST .../login
     * 包含两个参数：
     *      1. userPhone 用户手机号
     *      2. password 登录密码
     * 两个参数都以request param的方式传递
     *      POST .../login?userPhone=[用户手机号]&password=[登录密码]
     * 返回结果
     *      成功：
     *          body 中返回 {
     *                      "code": "200",
     *                      "msg": "登录成功",
     *                      "data": {
     *                          "id": "",
     *                          "locked": "",
     *                          "userPhone": "",
     *                          "name": "",
     *                          "idNumber": "",
     *                          "role": ""
     *                      }
     *                  }
     *          header 中 Authorization 带有 jwt token
     *      失败：
     *          body中返回 {
     *                          "code": "401",
     *                          "msg": "登录失败",
     *                          "data": null
     *                  }
     * */
    @PostMapping
    public Mono<ResponseEntity<ResultEntity<HealthBoxUserVO>>> login(
            @RequestParam(name = "userPhone") String userPhone,
            @RequestParam(name = "password") String password
    ) {

        log.info("user " + userPhone + " is logging in with password: " + password);
        Mono<UserDetails> user = userDetailsService.findByUsername(userPhone);
        return user
                .filter(userDetails -> passwordEncoder
                        .matches(password, userDetails.getPassword()))
                .switchIfEmpty(Mono.empty())
                .map(userDetails -> {
                            log.info("user phone: " + userPhone + " and password: " + password
                                    + " matched successfully");
                            HealthBoxUser userInfo = (HealthBoxUser) userDetails;
                            HealthBoxUserVO vo = userInfo.toVO();
                            log.info("got user info: " + vo);
                            log.info("generating new jwt token for user: " + userPhone);
                            String token = healthBoxJwt.generateToken(userDetails);
                            log.info("generated token: " + token + " for user: " + userPhone);
                            return ResponseEntity
                                    .status(HttpStatus.OK)
                                    .header(HttpHeaders.AUTHORIZATION, token)
                                    .body(ResultEntity.ok().msg("登录成功").data(vo));
                        }
                )
                .onErrorResume(UsernameNotFoundException.class,
                        e -> userPhoneError(userPhone, password))
                .switchIfEmpty(passwordError(userPhone, password));
    }

    private Mono<ResponseEntity<ResultEntity<HealthBoxUserVO>>> passwordError(
            String userPhone, String password) {
        log.error("user phone: " + userPhone + " and password: " + password
                + " did`nt match, logging in failed");
        return Mono.just(ResponseEntity.status(HttpStatus.OK)
                .body(ResultEntity.unauthorized().msg("密码错误, 登录失败").build()));
    }

    private Mono<ResponseEntity<ResultEntity<HealthBoxUserVO>>> userPhoneError(
            String userPhone, String password) {
        log.error("user phone " + userPhone + " not found");
        return Mono.just(ResponseEntity.status(HttpStatus.OK)
                .body(ResultEntity.unauthorized().msg("用户名错误, 登录失败").build()));
    }

}
